package org.ycsoft.PathComputation;

import java.util.*;

import org.ycsoft.MetroidReader.Commons;

/**
 * Represents a transportation line in a city map. A line is essentially a 
 * list of stops. A line is uniquely identified by its id and it has a line 
 * name.
 * @author allad
 *
 */
public class Line implements MetroidEntity{
    
    private String lineName;
    private int lineId;   //unique Id representing this line object
    private int frequency;  //line frequency in seconds
    private int totalTime;  //line overall travel time in seconds
    
    private static int idCounter = 1;  //class wise variable used for ensuring unicity of ids
    
    private boolean bidirectional = true; //by default line goes in both directions
    
    private List<Stop> stops = new ArrayList<Stop>();
    
    private Line inverseLine; //line heading in the inverse direction
    private Stop terminus;  //last stop in this line direction
    
    
    
    public Line(String lineName,int totalTime,int frequency){
        if(lineName != null){
        	this.lineName = lineName.trim();
        	this.frequency = frequency;
            this.totalTime = totalTime;
            this.lineId = idCounter++; //the id counters is incremented to avoid using the same id twice
        }
        else {
        	this.lineName = null;
        	this.frequency = frequency;
        	this.totalTime = totalTime;
        	this.lineId = -1;
        	
        }
        
    }
    
    
    /**
     * To be called when all stops have been added to the line
     * Performs wrap up operations like setting the terinus and the inverse line
     */
    public void endStops(){
    	if(bidirectional){
    		if(inverseLine == null)
    			inverseLine = new Line(this.lineName,this.totalTime,this.frequency);
    		else{
    			inverseLine.setTotalTime(totalTime);
    			inverseLine.setFrequency(frequency);
    		}
    		
    		//add stops in reverse order
    		inverseLine.stops.clear();
    		for(int i=stops.size()-1;i>=0;i--){
    			inverseLine.stops.add(stops.get(i));
    		}
    		//set terminus
    		if(stops.size()>=1)	
    			inverseLine.terminus = stops.get(0);    		 
    	}
    	else inverseLine = null;
    	
    	//set this line terminus
    	if(stops.size()>=1)
    		terminus = stops.get(stops.size()-1);
    }
    
    /**
     * @param lineName Name of this line object
     */
    public Line(String lineName){
    	this(lineName,0,0);
    }
    
    
    /**
     * @return the line name
     */
    public String getLineName(){
        return lineName;
    }
    
    /**
     * @return Indicates whether the line goes in both directions
     */
    public boolean isBidirectional(){
    	return bidirectional;
    }
    
    
    /**
     * Set whether this line is bidirectional or not
     * @param bidirectional
     */
    public void setBidirectional(boolean bidirectional){
    	this.bidirectional = bidirectional;
    }
    
    /**
     * @return The last stop in this line
     */
    public Stop getTerminus(){
    	return terminus;
    }
    
    /**
     * @return the line id
     */
    public int getLineId(){
        return lineId;
    }
    
    /**
     * @return The total time needed to travel this line in seconds
     */
    public int getTotalTime(){
    	return totalTime;
    }
    
    /**
     * Set the line overall travel time
     * @param totalTime Total Time in seconds
     */
    public void setTotalTime(int totalTime){
    	this.totalTime = totalTime;
    }
    
    /**
     * @return The line frequency in seconds
     */
    public int getFrequency(){
    	return frequency;
    }
    
    /**
     * Sets the line frequency
     * @param frequency Frequency in seconds
     */
    public void setFrequency(int frequency){
    	this.frequency = frequency;
    }
    
    /**
     * @return the average time per stop in seconds
     */
    public int getAverageTimePerStop(){
    	if(stops.size() == 0 || totalTime <= 0)
    		return 5*60; //by default 5 secs
    	else{
    		return totalTime/stops.size();
    	}
    }
    
    /**
     * @return The line containing the stops ordered in the other direction around
     */
    public Line getInverseLine(){
    	if(bidirectional){
    		if(inverseLine == null)
    			inverseLine = new Line(this.lineName,this.totalTime,this.frequency);
    		return inverseLine;
    	}
    	else return null;
    }
    
    /**
     * Adds a stops to the stops list of this line
     * @param stop Stop object to be added
     */
    public void addStop(Stop stop){
        if(stop != null){
            stops.add(stop);
        }
    }
    
    /**
     * @return the stops list of this Line
     */
    public List<Stop> getStops(){
        return stops;
    }
    
    /* (non-Javadoc)
     * @see java.lang.Object#equals(java.lang.Object)
     */
    public boolean equals(Object o){
    	if(o == null){
    		return false;
    	}
    	if(o instanceof Line){
    		Line other = (Line)o;
    		return other.lineId == this.lineId;
    	}
    	else return false;
    		
    }
    
    
    /* (non-Javadoc)
     * @see java.lang.Object#toString()
     */
    public String toString(){
    	String tmp = "Line : "+lineName+"|"+lineId;
    	if(inverseLine != null){
    		tmp += "\nInverse : "+inverseLine.lineId;
    	}
    	return tmp;
    }

	/* (non-Javadoc)
	 * @see org.ycsoft.PathComputation.MetroidEntity#toMetroid()
	 */
	public String toMetroid() {
		//Format
		//id
		//name
		//frequency
		//terminus
		//stops
		StringBuilder sb = new StringBuilder();
		sb.append("line"+Commons.LINE_SEPARATOR);
		sb.append(""+lineId+Commons.LINE_SEPARATOR);
		sb.append(""+lineName+Commons.LINE_SEPARATOR);
		sb.append(""+frequency+Commons.LINE_SEPARATOR);
		sb.append(""+terminus.getStopName()+Commons.LINE_SEPARATOR);
		//liste des stops
		for(int i=0;i<stops.size()-1;i++)
			sb.append(stops.get(i).getStopId()+",");
		sb.append(stops.get(stops.size()-1).getStopId());
		sb.append(Commons.LINE_SEPARATOR);
		
		sb.append("end"+Commons.LINE_SEPARATOR);
		
		return sb.toString();
	}
} 
